package com.axinfu.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * util:file
 *
 * @author zjn
 * @since 2022/3/23
 */
@SuppressWarnings("unused")
public final class FileUtil {

    public static final String ENCODING_UTF_8 = "UTF-8";
    public static final String ENCODING_GBK = "GBK";

    private FileUtil() {
    }

    /**
     * 判断文件是否存在
     *
     * @param file 文件对象
     * @return 是返回true，否返回false
     */
    public static boolean isExists(File file) {
        return file.exists();
    }

    /**
     * 判断文件是否存在
     *
     * @param filePath 文件路径
     * @return 是返回true，否返回false
     */
    public static boolean isExists(String filePath) {
        return isExists(new File(filePath));
    }

    /**
     * 判断文件是否不存在
     *
     * @param file 文件对象
     * @return 是返回true，否返回false
     */
    public static boolean isNotExists(File file) {
        return !isExists(file);
    }

    /**
     * 判断文件是否不存在
     *
     * @param filePath 文件路径
     * @return 是返回true，否返回false
     */
    public static boolean isNotExists(String filePath) {
        return !isExists(filePath);
    }

    /**
     * 判断文件是否文件夹
     *
     * @param file 文件对象
     * @return 是返回true，否返回false
     */
    public static boolean isDirectory(File file) {
        if (isNotExists(file)) {
            return false;
        }
        return file.isDirectory();
    }

    /**
     * 判断文件是否文件夹
     *
     * @param filePath 文件路径
     * @return 是返回true，否返回false
     */
    public static boolean isDirectory(String filePath) {
        return isDirectory(new File(filePath));
    }

    /**
     * 判断文件是否不是文件夹
     *
     * @param file 文件对象
     * @return 是返回true，否返回false
     */
    public static boolean isNotDirectory(File file) {
        return !isDirectory(file);
    }

    /**
     * 判断文件是否不是文件夹
     *
     * @param filePath 文件路径
     * @return 是返回true，否返回false
     */
    public static boolean isNotDirectory(String filePath) {
        return !isDirectory(filePath);
    }

    /**
     * 获取文件规范路径
     *
     * @param file 文件对象
     * @return 路径
     */
    public static String getCanonicalPath(File file) {
        try {
            return file.getCanonicalPath();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取文件规范路径
     *
     * @param path 文件夹路径
     * @return 路径
     */
    public static String getCanonicalPath(String path) {
        return getCanonicalPath(new File(path));
    }

    /**
     * 获取文件后缀名
     *
     * @param file 文件对象
     * @return 后缀名
     */
    public static String getSuffix(File file) {
        String fileName = file.getName();
        return fileName.substring(fileName.lastIndexOf(".") + 1);
    }

    /**
     * 获取文件后缀名
     *
     * @param path 文件夹路径
     * @return 后缀名
     */
    public static String getSuffix(String path) {
        return getSuffix(new File(path));
    }

    /**
     * 创建文件夹
     *
     * @param file 文件夹对象
     */
    public static void mkDir(File file) {

        if (isDirectory(file)) {
            return;
        }

        if (isExists(file)) {
            throw new RuntimeException("file exist and not directory," + file.getAbsolutePath());
        }

        if (!file.mkdirs()) {
            throw new RuntimeException("directory does not exist and create the failure," + file.getAbsolutePath());
        }
    }

    /**
     * 创建文件夹
     *
     * @param path 文件夹路径
     */
    public static void mkDir(String path) {
        mkDir(new File(path));
    }

    /**
     * 删除文件或文件夹
     *
     * @param file 文件或文件夹
     */
    public static void deleteFile(File file) {
        if (isNotExists(file)) {
            return;
        }
        if (!file.delete()) {
            throw new RuntimeException("delete file failure," + file.getAbsolutePath());
        }
    }

    /**
     * 删除文件或文件夹
     *
     * @param filePath 文件或文件夹
     */
    public static void deleteFile(String filePath) {
        deleteFile(new File(filePath));
    }

    /**
     * 移除文件或文件夹
     *
     * @param file      文件或文件夹
     * @param isRmChild 当为文件夹时，是否移除下级文件或文件夹
     */
    public static void rmFile(File file, boolean isRmChild) {

        if (isNotExists(file)) {
            return;
        }

        if (isDirectory(file)) {
            File[] childFiles = file.listFiles();

            if (EmptyUtil.isNotEmpty(childFiles)) {

                if (!isRmChild) {
                    throw new RuntimeException("directory not empty," + file.getAbsolutePath());
                }

                for (File childFile : childFiles) {
                    rmFile(childFile, true);
                }
            }
        }

        deleteFile(file);
    }

    /**
     * 移除文件或文件夹（当存在下级文件文件夹时不允许操作）
     *
     * @param file 文件或文件夹
     */
    public static void rmFile(File file) {
        rmFile(file, false);
    }

    /**
     * 移除文件或文件夹
     *
     * @param filePath   文件或文件夹路径
     * @param isRmChilds 当为文件夹时，是否移除下级文件或文件夹
     */
    public static void rmFile(String filePath, boolean isRmChilds) {
        rmFile(new File(filePath), isRmChilds);
    }

    /**
     * 移除文件或文件夹（当存在下级文件文件夹时不允许操作）
     *
     * @param filePath 文件或文件夹路径
     */
    public static void rmFile(String filePath) {
        rmFile(filePath, false);
    }

    /**
     * 清空文件夹
     *
     * @param file   文件夹
     * @param rmSelf 是否删除自身
     */
    public static void clearDir(File file, boolean rmSelf) {

        if (isNotDirectory(file)) {
            return;
        }

        if (rmSelf) {
            rmFile(file, true);
            return;
        }

        File[] childFiles = file.listFiles();
        if (EmptyUtil.isNotEmpty(childFiles)) {
            for (File childFile : childFiles) {
                rmFile(childFile, true);
            }
        }
    }

    /**
     * 清空文件夹（不删除自身）
     *
     * @param file 文件夹
     */
    public static void clearDir(File file) {
        clearDir(file, false);
    }

    /**
     * 清空文件夹
     *
     * @param filePath 文件夹路径
     * @param rmSelf   是否删除自身
     */
    public static void clearDir(String filePath, boolean rmSelf) {
        clearDir(new File(filePath), rmSelf);
    }

    /**
     * 清空文件夹（不删除自身）
     *
     * @param filePath 文件夹路径
     */
    public static void clearDir(String filePath) {
        clearDir(filePath, false);
    }

    /**
     * 创建文件
     *
     * @param file 文件对象或文件夹
     */
    public static void createFile(File file) {
        if (isExists(file)) {
            return;
        }

        mkDir(file.getParentFile());

        try {
            if (!file.createNewFile()) {
                throw new RuntimeException("create file error：" + file.getAbsolutePath());
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 创建文件
     *
     * @param path 文件路径
     */
    public static void createFile(String path) {
        createFile(new File(path));
    }

    /**
     * 拷贝文件
     *
     * @param file     文件对象
     * @param copyFile 目标文件对象
     */
    public static void copyTo(File file, File copyFile) {
        try {
            if (isNotExists(file)) {
                throw new RuntimeException("file not exists," + file.getPath());
            }

            if (isDirectory(file)) {
                throw new RuntimeException("file is a directory," + file.getAbsolutePath());
            }

            mkDir(copyFile.getParentFile());

            FileInputStream in = new FileInputStream(file);
            FileOutputStream out = new FileOutputStream(copyFile);
            byte[] buffer = new byte[1024];
            int ins;
            while ((ins = in.read(buffer)) != -1) {
                out.write(buffer, 0, ins);
            }
            out.close();
            in.close();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 拷贝文件
     *
     * @param filePath     文件对象路径
     * @param copyFilePath 目标文件对象路径
     */
    public static void copyTo(String filePath, String copyFilePath) {
        copyTo(new File(filePath), new File(copyFilePath));
    }

    /**
     * 拷贝文件
     *
     * @param file         文件对象
     * @param copyFilePath 目标文件对象路径
     */
    public static void copyTo(File file, String copyFilePath) {
        copyTo(file, new File(copyFilePath));
    }

    /**
     * 拷贝文件
     *
     * @param filePath 文件对象路径
     * @param copyFile 目标文件对象
     */
    public static void copyTo(String filePath, File copyFile) {
        copyTo(new File(filePath), copyFile);
    }

    /**
     * 拷贝文件或文件夹到指定目录
     *
     * @param file         文件或文件夹
     * @param targetDir    目标文件夹
     * @param containsRoot 是否包含自身（对文件夹有效）
     */
    public static void copyToDir(File file, File targetDir, boolean containsRoot) {
        if (isNotExists(file)) {
            throw new RuntimeException("file not exists," + file.getPath());
        }

        if (isNotExists(targetDir)) {
            mkDir(targetDir);
        }

        if (isNotDirectory(targetDir)) {
            throw new RuntimeException("file is not a directory," + targetDir.getAbsolutePath());
        }

        if (isDirectory(file)) {

            if (containsRoot) {
                copyToDir(file, targetDir + File.separator + file.getName(), false);
                return;
            }

            File[] childFiles = file.listFiles();
            if (EmptyUtil.isNotEmpty(childFiles)) {
                for (File childFile : childFiles) {
                    File childTargetDir;
                    if (isDirectory(childFile)) {
                        childTargetDir = new File(targetDir + File.separator + childFile.getName());
                    } else {
                        childTargetDir = targetDir;
                    }
                    copyToDir(childFile, childTargetDir, false);
                }
            }
            return;
        }

        File targetFile = new File(targetDir + File.separator + file.getName());

        copyTo(file, targetFile);
    }

    /**
     * 拷贝文件或文件夹到指定目录
     *
     * @param filePath      文件或文件夹路径
     * @param targetDirPath 目标文件夹路径
     * @param containsRoot  是否包含自身
     */
    public static void copyToDir(String filePath, String targetDirPath, boolean containsRoot) {
        copyToDir(new File(filePath), new File(targetDirPath), containsRoot);
    }

    /**
     * 拷贝文件或文件夹到指定目录
     *
     * @param filePath      文件或文件夹路径
     * @param targetDirPath 目标文件夹路径
     */
    public static void copyToDir(String filePath, String targetDirPath) {
        copyToDir(new File(filePath), new File(targetDirPath), false);
    }

    /**
     * 拷贝文件或文件夹到指定目录
     *
     * @param file          文件或文件夹
     * @param targetDirPath 目标文件夹路径
     * @param containsRoot  是否包含自身
     */
    public static void copyToDir(File file, String targetDirPath, boolean containsRoot) {
        copyToDir(file, new File(targetDirPath), containsRoot);
    }

    /**
     * 拷贝文件或文件夹到指定目录
     *
     * @param file          文件或文件夹
     * @param targetDirPath 目标文件夹路径
     */
    public static void copyToDir(File file, String targetDirPath) {
        copyToDir(file, new File(targetDirPath), false);
    }

    /**
     * 拷贝文件或文件夹到指定目录
     *
     * @param filePath     文件或文件夹路径
     * @param targetDir    目标文件夹
     * @param containsRoot 是否包含自身
     */
    public static void copyToDir(String filePath, File targetDir, boolean containsRoot) {
        copyToDir(new File(filePath), targetDir, containsRoot);
    }

    /**
     * 拷贝文件或文件夹到指定目录
     *
     * @param filePath  文件或文件夹路径
     * @param targetDir 目标文件夹
     */
    public static void copyToDir(String filePath, File targetDir) {
        copyToDir(new File(filePath), targetDir, false);
    }

    /**
     * 从指定文件中读取内容
     *
     * @param is 输入流
     * @return 内容
     */
    @SuppressWarnings("all")
    public static byte[] read(InputStream is) {
        byte[] buffer;
        try {
            int size = is.available();
            buffer = new byte[size];
            is.read(buffer);
            is.close();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return buffer;
    }

    /**
     * 从指定文件中读取内容
     *
     * @param file 文件
     * @return 内容
     */
    public static byte[] read(File file) {
        if (isNotExists(file)) {
            throw new RuntimeException("file not exists：" + file.getAbsolutePath());
        }
        if (isDirectory(file)) {
            throw new RuntimeException("file is a directory：" + file.getAbsolutePath());
        }

        try {
            InputStream is = new FileInputStream(file);
            return read(is);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 从指定文件中读取内容
     *
     * @param filePath 文件路径
     * @return 内容
     */
    public static byte[] read(String filePath) {
        File file = new File(filePath);
        return read(file);
    }

    /**
     * 从输入流中读取内容为字符串
     *
     * @param is       输入流
     * @param encoding 字符编码
     * @return 内容
     */
    public static String readToString(InputStream is, String encoding) {
        byte[] bytes = read(is);
        try {
            return new String(bytes, encoding);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 从输入流中读取内容为字符串
     *
     * @param is 输入流
     * @return 内容
     */
    public static String readToString(InputStream is) {
        return readToString(is, ENCODING_UTF_8);
    }

    /**
     * 从指定文件中读取内容为字符串
     *
     * @param file     文件
     * @param encoding 字符编码
     * @return 内容
     */
    public static String readToString(File file, String encoding) {
        byte[] bytes = read(file);
        try {
            return new String(bytes, encoding);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 从指定文件中读取内容为字符串
     *
     * @param file 文件
     * @return 内容
     */
    public static String readToString(File file) {
        return readToString(file, ENCODING_UTF_8);
    }

    /**
     * 从指定文件中读取内容为字符串
     *
     * @param filePath 文件路径
     * @param encoding 字符编码
     * @return 内容
     */
    public static String readToString(String filePath, String encoding) {
        File file = new File(filePath);
        return readToString(file, encoding);
    }

    /**
     * 从指定文件中读取内容为字符串
     *
     * @param filePath 文件路径
     * @return 内容
     */
    public static String readToString(String filePath) {
        return readToString(filePath, ENCODING_UTF_8);
    }

    /**
     * 将内容写入到指定文件
     *
     * @param is 输入流
     * @param os 输出流
     */
    public static void write(InputStream is, OutputStream os) {
        try {
            byte[] b = new byte[1024];
            int len;
            while ((len = is.read(b)) != -1) {
                os.write(b, 0, len);
            }
            os.close();
            is.close();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 将内容写入到指定文件
     *
     * @param os      输出流
     * @param content 内容
     */
    public static void write(OutputStream os, byte[] content) {
        try {
            os.write(content);
            os.close();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 将内容写入到指定文件
     *
     * @param os       输出流
     * @param content  内容
     * @param encoding 编码
     */
    public static void write(OutputStream os, String content, String encoding) {
        byte[] bytes;
        try {
            bytes = content.getBytes(encoding);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        write(os, bytes);
    }

    /**
     * 将内容写入到指定文件
     *
     * @param file    文件
     * @param content 内容
     */
    public static void write(File file, byte[] content) {
        try {
            if (isDirectory(file)) {
                throw new RuntimeException("file is a folder：" + file.getAbsolutePath());
            }

            mkDir(file.getParentFile());

            createFile(file);

            FileOutputStream os = new FileOutputStream(file);
            write(os, content);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 将内容写入到指定文件
     *
     * @param file     文件
     * @param content  内容
     * @param encoding 编码
     */
    public static void write(File file, String content, String encoding) {
        byte[] bytes;
        try {
            bytes = content.getBytes(encoding);
        } catch (Exception e) {
            throw new RuntimeException("write file error：" + file.getAbsolutePath(), e);
        }
        write(file, bytes);
    }

    /**
     * 将内容写入到指定文件
     *
     * @param file    文件
     * @param content 内容
     */
    public static void write(File file, String content) {
        write(file, content.getBytes());
    }

    /**
     * 将内容写入到指定文件
     *
     * @param filePath 文件路径
     * @param content  内容
     */
    public static void write(String filePath, byte[] content) {
        File file = new File(filePath);
        write(file, content);
    }

    /**
     * 将内容写入到指定文件
     *
     * @param filePath 文件路径
     * @param content  内容
     * @param encoding 编码
     */
    public static void write(String filePath, String content, String encoding) {
        byte[] bytes;
        try {
            bytes = content.getBytes(encoding);
        } catch (Exception e) {
            throw new RuntimeException("write file error：" + filePath, e);
        }
        write(filePath, bytes);
    }

    /**
     * 将内容写入到指定文件
     *
     * @param filePath 文件路径
     * @param content  内容
     */
    public static void write(String filePath, String content) {
        write(filePath, content, ENCODING_UTF_8);
    }
}